import random
from datetime import datetime
import numpy as np
from ortools.sat.python import cp_model
import copy


def generate_data():
    # 随机生成数据

    hot = [0, 1, 2]
    layers = 37
    probability = 0.5

    # 定义货格的层次关系，使用二维列表表示货格布局，即模板号的排列规则
    each_row = ['10'] * 4 + ['8'] * 1 + ['10'] * 4 + ['6'] * 2 + ['10'] * 3 + ['8'] * 1 + ['10'] * 4 + ['6'] * 1 + [
        '10'] * 2 + ['8'] * 3 + ['10'] * 4 + ['4'] * 1 + ['10'] * 5 + ['6'] * 2 + \
               ['10'] * 4 + ['8'] * 1 + ['10'] * 4 + ['6'] * 2 + ['10'] * 3 + ['8'] * 1 + ['10'] * 4 + ['6'] * 1 + [
                   '10'] * 2 + ['8'] * 3 + ['10'] * 4 + ['4'] * 1 + ['10'] * 5 + ['6'] * 1
    cargo_grid_mapping = [each_row] * layers

    # 定义不同模板号的可放置的货物类型，以及每种货物类型最多放多少货物
    template_mapping = {"265": {
        "10": {"265*600*400": 10, "265*600*315": 12, "265*400*400": 15, "265*400*315": 18, "265*400*255": 21},
        "8": {"265*600*400": 8, "265*400*400": 12, "265*400*350": 12, "265*400*255": 15},
        "6": {"265*600*400": 6, "265*400*400": 9, "265*400*350": 12},
        "4": {"265*600*400": 4, "265*400*400": 6}
    },
        "330": {
            "10": {"330*600*400": 10, "330*600*315": 12, "330*400*400": 15, "330*400*315": 18, "330*400*255": 21},
            "8": {"330*600*400": 8, "330*400*400": 12, "330*400*350": 12, "330*400*255": 15},
            "6": {"330*600*400": 6, "330*400*400": 9, "330*400*350": 12},
            "4": {"330*600*400": 4, "330*400*400": 6}
        },
        "400": {
            "10": {"400*600*400": 10, "400*600*315": 12, "400*400*400": 15, "400*400*315": 18, "400*400*255": 21},
            "8": {"400*600*400": 8, "400*400*400": 12, "400*400*350": 12, "400*400*255": 15},
            "6": {"400*600*400": 6, "400*400*400": 9, "400*400*350": 12},
            "4": {"400*600*400": 4, "400*400*400": 6}
        },
        "500": {
            "10": {"500*600*400": 10, "500*600*315": 12, "500*400*400": 15, "500*400*315": 18, "500*400*255": 21},
            "8": {"500*600*400": 8, "500*400*400": 12, "500*400*350": 12, "500*400*255": 15},
            "6": {"500*600*400": 6, "500*400*400": 9, "500*400*350": 12},
            "4": {"500*600*400": 4, "500*400*400": 6}
        }
    }

    high = ['500', '330', '330', '400', '330', '330', '265', '265', '265', '400', '330', '330', '265', '265', '265',
            '400',
            '330', '330', '265', '265', '265', '400', '330', '330', '265', '265', '265', '400', '330', '330', '265',
            '265',
            '265', '400', '265', '265', '265']

    # # 将所有类型统一到一个字典
    # template_mapping_combined = {}
    # for key, value in template_mapping.items():
    #     template_mapping_combined.update(value)
    # # 将所有类型名整理到一个列表
    # available_goods_out = list(template_mapping_combined.keys())
    # # 定义一个空二维数组
    inner_keys = []
    for key1 in template_mapping:
        for key2 in template_mapping[key1]:
            inner_keys.extend(template_mapping[key1][key2].keys())
    # 将所有类型名整理到一个列表
    available_goods_out = []
    for key in inner_keys:
        if key not in available_goods_out:
            available_goods_out.append(key)
    print(available_goods_out)

    # 计算所有模板的货量最大值总和
    a = template_mapping['265']
    sum_total = 0
    for key, value in a.items():
        for key_1, value_1 in value.items():
            sum_total = sum_total + value_1
    print(sum_total)

    result = np.zeros((layers, len(each_row)), dtype=object)
    # 随机为每个货格选择货物类型和货物数量
    for row in range(layers):
        for grid in range(len(each_row)):
            template = cargo_grid_mapping[row][grid]
            if random.random() < probability:
                available_goods = list(template_mapping[high[row]][template].keys())
                selected_goods = random.choice(available_goods)
                max_quantity = template_mapping[high[row]][template][selected_goods]
                selected_quantity = random.randint(1, max_quantity)
                result[row, grid] = (
                    layers - row, grid + 1, template, selected_goods, selected_quantity, random.choice(hot), high[row])
            else:
                result[row, grid] = (layers - row, grid + 1, template, 0, 0, random.choice(hot), high[row])

    # 将结果展开为一维
    result_flatten = result.flatten()

    # 打印结果，输出为一个二维列表，列表元素为元组,数组内的元素为元组（层数，货格序号，货格号，选取的货物类型，货量，热度，高度）
    for i in range(layers):
        print(result[i, :])

    # 计算货格总数
    element_count = result.size
    print(element_count)

    return result_flatten, element_count, available_goods_out, template_mapping, sum_total


class Logger(object):
    """
    Mock Interface
    Logger
    """
    @staticmethod
    def info(msg):
        print(f'[INFO] {Logger.date_str(datetime.now())} {msg}')

    @staticmethod
    def error(msg):
        print(f'[ERROR] {Logger.date_str(datetime.now())} {msg}')

    @staticmethod
    def date_str(date: datetime):
        return date.strftime('%Y-%m-%d %H:%M:%S') if date else None


#确定下限的松弛
def do_solve1(result_flatten, element_count, available_goods_out, template_mapping, sum_total, ahj, amj, alj, logger=Logger):
    # 求解


    f = 0.85
    logger.info(f'do_solve1求解开始')
    while True:

        '''
        :param result_flatten:一个一维列表，列表中的元素为元组，从货架顶自左向右，自上而下遍历所有货格，每个元组内的元素为：（层数，货格序号，货格号，选取的货物类型，货量，热度，高度）
        :param element_count:货格总数
        :param available_goods_out:一个一维列表，列表元素为该货架所有的模板类型，格式为“高度*长*宽”
        :param template_mapping:一个字典，定义不同模板号的可放置的货物类型，以及每种货物类型最多放多少货物
        :param sum_total:不考虑高度，所有模板最大放货量的总和，此变量主要是为了给求解器一个范围值，也可用100代替
        :return:每个货格选择的模板
        '''
        N = element_count
        M = len(available_goods_out)

        # 创建cp-sat模型
        model = cp_model.CpModel()
        logger.info(f'确定下限约束模型创建完成')

        # 变量Xij表示是否选中货格i的货物类型j，采用布尔类型
        X = {}
        for i in range(1, N + 1):
            for j in range(1, M + 1):
                X[(i, j)] = model.NewBoolVar(f'X_{i}_{j}')

        # 判断是否为异常货格
        exi = np.zeros(N + 1, dtype=int)

        # 创建参数nij，货格i的包规j模板可提供的货位数
        nij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 nij 数组
        nij[0, 1:] = available_goods_out
        nij[1:, 0] = list(range(1, element_count + 1))
        for i in range(1, N + 1):  # 将i的范围从1到N+1
            if result_flatten[i - 1][2] not in template_mapping[result_flatten[i - 1][6]]:  # 检查货格号是否在可用模板的货格号中，若不存在则为异常货格，exi[i] = 1
                exi[i] = 1
            else:
                for j in range(1, M + 1):  # 将j的范围从1到M+1
                    if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货，nij中就是对应模板可放最大货位数
                        if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][
                            result_flatten[i - 1][2]]:
                            nij[i][j] = template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                available_goods_out[j - 1]]
                        else:
                            nij[i][j] = 0
                            model.Add(X[(i, j)] == 0)
                    else:  # 如果货格 i 中已经有货，货物类型为 selected_goods，nij中就是对应模板可放最大货位数-已放货位数
                        if result_flatten[i - 1][3] == available_goods_out[j - 1]:
                            max_quantity = template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                result_flatten[i - 1][3]]
                            selected_quantity = result_flatten[i - 1][4]
                            nij[i][j] = max_quantity - selected_quantity
                        else:
                            if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][
                                result_flatten[i - 1][2]]:
                                nij[i][j] = template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                    available_goods_out[j - 1]]
                            else:
                                nij[i][j] = 0  # 其他类型的货物不能放在已经有货的货格中
                                model.Add(X[(i, j)] == 0)

        # # 创建参数mij
        # mij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 mij 数组
        # mij[0, 1:] = available_goods_out
        # mij[1:, 0] = list(range(1, element_count + 1))
        # d = 3
        # for i in range(1, N + 1):  # 将i的范围从1到N+1
        #     for j in range(1, M + 1):  # 将j的范围从1到M+1
        #         if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
        #             mij[i][j] = 0
        #         else:
        #             if result_flatten[i - 1][3] == available_goods_out[j - 1]:
        #                 if result_flatten[i - 1][4] <= d:
        #                     mij[i][j] = 1
        #                 else:
        #                     mij[i][j] = 0
        #             else:
        #                 mij[i][j] = 0

        # 创建参数khi,kmi,kli,判断货格i是否为高中低热度
        khi = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][5] == 2:
                khi[i] = 1
            else:
                khi[i] = 0

        kmi = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][5] == 1:
                kmi[i] = 1
            else:
                kmi[i] = 0

        kli = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][5] == 0:
                kli[i] = 1
            else:
                kli[i] = 0

        # 创建参数,判断货格i是否为对应高度
        h265 = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][6] == '265':
                h265[i] = 1
            else:
                h265[i] = 0

        h330 = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][6] == '330':
                h330[i] = 1
            else:
                h330[i] = 0

        h400 = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][6] == '400':
                h400[i] = 1
            else:
                h400[i] = 0

        h500 = np.zeros(N + 1, dtype=int)
        for i in range(1, N + 1):
            if result_flatten[i - 1][6] == '500':
                h500[i] = 1
            else:
                h500[i] = 0

        # # 创建参数o(有货的货格总数）
        # o = 0
        # for i in range(1, N + 1):  # 将i的范围从1到N+1
        #     if result_flatten[i - 1][3] != 0:
        #         o += 1
        #
        # # 创建参数e
        # e = 1

        # 创建参数Q
        Q = 100000

        # # 约束条件：模板调整涉及到的货量尽量少
        # model.Add(
        #     sum(mij[i][j] - X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1) if mij[i][j] == 1) <= int(
        #         e * o))

        # 约束条件 - 每个货格只能选择一个货物类型
        for i in range(1, N + 1):
            model.Add(sum(X[(i, j)] for j in range(1, M + 1)) == 1)

        # 计算总货位数
        total_cargo_space = model.NewIntVar(0, sum_total * N, 'total_cargo_space')

        # 添加约束条件，计算总货位数
        model.Add(total_cargo_space == sum(nij[i][j] * X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1)))

        # 比例约束是同高同热度的一组比例，即同高同热度所有模板比例相加为1
        # 新增约束（高热度部分）下限
        for J in range(1, M + 1):
            if available_goods_out[J - 1][:3] == '265':
                model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              khi[i] == 1 and h265[i] == 1) * int(ahj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '330':
                model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              khi[i] == 1 and h330[i] == 1) * int(ahj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '400':
                model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              khi[i] == 1 and h400[i] == 1) * int(ahj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '500':
                model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              khi[i] == 1 and h500[i] == 1) * int(ahj[J] * Q * f)
                          )
        # 新增约束（中热度部分）下限
        for J in range(1, M + 1):
            if available_goods_out[J - 1][:3] == '265':
                model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kmi[i] == 1 and h265[i] == 1) * int(amj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '330':
                model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kmi[i] == 1 and h330[i] == 1) * int(amj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '400':
                model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kmi[i] == 1 and h400[i] == 1) * int(amj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '500':
                model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kmi[i] == 1 and h500[i] == 1) * int(amj[J] * Q * f)
                          )

        # 新增约束（低热度部分）下限
        for J in range(1, M + 1):
            if available_goods_out[J - 1][:3] == '265':
                model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kli[i] == 1 and h265[i] == 1) * int(alj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '330':
                model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kli[i] == 1 and h330[i] == 1) * int(alj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '400':
                model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kli[i] == 1 and h400[i] == 1) * int(alj[J] * Q * f)
                          )
            if available_goods_out[J - 1][:3] == '500':
                model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                          sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                              kli[i] == 1 and h500[i] == 1) * int(alj[J] * Q * f)
                          )


        # 保证比例为0的不选择
        for j in range(1, M + 1):
            if ahj[j] == 0:
                for i in range(1, N + 1):
                    if khi[i] == 1:
                        model.Add(X[(i, j)] == 0)

        for j in range(1, M + 1):
            if amj[j] == 0:
                for i in range(1, N + 1):
                    if kmi[i] == 1:
                        model.Add(X[(i, j)] == 0)

        for j in range(1, M + 1):
            if alj[j] == 0:
                for i in range(1, N + 1):
                    if kli[i] == 1:
                        model.Add(X[(i, j)] == 0)

        logger.info(f'下限约束添加完成')

        # 优化目标 - 最大化总货位数
        model.Maximize(total_cargo_space)

        # 创建一个cp-sat求解器
        solver = cp_model.CpSolver()
        solver.parameters.log_search_progress = False
        solver.parameters.max_time_in_seconds = 200
        # 解决优化问题
        status = solver.Solve(model)
        logger.info(f'下限约束模型试求解')
        # 输出结果
        if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
            logger.info(f'下限约束解决')
            return f
        else:
            if f <= 0.1:
                logger.info(f'下限约束不满足')
                return 0
            else:
                f = f - 0.01

#确定上限松弛
def do_solve2(result_flatten, element_count, available_goods_out, template_mapping, sum_total, ahj, amj, alj, logger=Logger):
    # 求解

    f = do_solve1(result_flatten, element_count, available_goods_out, template_mapping, sum_total, ahj, amj, alj, logger=logger)
    logger.info(f'do_solve2求解开始')
    if f == 0:
        return 0, 0
    else:
        f = round(f, 2) - 0.01
        s = 1.5
        while True:

            '''
            :param result_flatten:一个一维列表，列表中的元素为元组，从货架顶自左向右，自上而下遍历所有货格，每个元组内的元素为：（层数，货格序号，货格号，选取的货物类型，货量，热度，高度）
            :param element_count:货格总数
            :param available_goods_out:一个一维列表，列表元素为该货架所有的模板类型，格式为“高度*长*宽”
            :param template_mapping:一个字典，定义不同模板号的可放置的货物类型，以及每种货物类型最多放多少货物
            :param sum_total:不考虑高度，所有模板最大放货量的总和，此变量主要是为了给求解器一个范围值，也可用100代替
            :return:每个货格选择的模板
            '''

            K = 100  # 抵消比例的100倍数
            N = element_count
            M = len(available_goods_out)

            # 创建cp-sat模型
            model = cp_model.CpModel()
            logger.info(f'确定上限约束模型创建完成')

            # 变量Xij表示是否选中货格i的货物类型j，采用布尔类型
            X = {}
            for i in range(1, N + 1):
                for j in range(1, M + 1):
                    X[(i, j)] = model.NewBoolVar(f'X_{i}_{j}')

            # 判断是否为异常货格
            exi = np.zeros(N + 1, dtype=int)

            # 创建参数nij
            nij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 nij 数组
            nij[0, 1:] = available_goods_out
            nij[1:, 0] = list(range(1, element_count + 1))
            for i in range(1, N + 1):  # 将i的范围从1到N+1
                if result_flatten[i - 1][2] not in template_mapping[result_flatten[i - 1][6]]:
                    exi[i] = 1
                else:
                    for j in range(1, M + 1):  # 将j的范围从1到M+1
                        if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
                            if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][
                                result_flatten[i - 1][2]]:
                                nij[i][j] = \
                                template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                    available_goods_out[j - 1]]
                            else:
                                nij[i][j] = 0
                                model.Add(X[(i, j)] == 0)
                        else:  # 如果货格 i 中已经有货，货物类型为 selected_goods
                            if result_flatten[i - 1][3] == available_goods_out[j - 1]:
                                max_quantity = \
                                template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                    result_flatten[i - 1][3]]
                                selected_quantity = result_flatten[i - 1][4]
                                nij[i][j] = max_quantity - selected_quantity
                            else:
                                if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][
                                    result_flatten[i - 1][2]]:
                                    nij[i][j] = \
                                    template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                        available_goods_out[j - 1]]
                                else:
                                    nij[i][j] = 0  # 其他类型的货物不能放在已经有货的货格中
                                    model.Add(X[(i, j)] == 0)


            # # 创建参数mij
            # mij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 mij 数组
            # mij[0, 1:] = available_goods_out
            # mij[1:, 0] = list(range(1, element_count + 1))
            # d = 3
            # for i in range(1, N + 1):  # 将i的范围从1到N+1
            #     for j in range(1, M + 1):  # 将j的范围从1到M+1
            #         if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
            #             mij[i][j] = 0
            #         else:
            #             if result_flatten[i - 1][3] == available_goods_out[j - 1]:
            #                 if result_flatten[i - 1][4] <= d:
            #                     mij[i][j] = 1
            #                 else:
            #                     mij[i][j] = 0
            #             else:
            #                 mij[i][j] = 0

            # 创建参数khi,kmi,kli,判断货格i是否为高中低热度
            khi = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][5] == 2:
                    khi[i] = 1
                else:
                    khi[i] = 0

            kmi = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][5] == 1:
                    kmi[i] = 1
                else:
                    kmi[i] = 0

            kli = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][5] == 0:
                    kli[i] = 1
                else:
                    kli[i] = 0

            # 创建参数,判断货格i是否为对应高度
            h265 = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][6] == '265':
                    h265[i] = 1
                else:
                    h265[i] = 0

            h330 = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][6] == '330':
                    h330[i] = 1
                else:
                    h330[i] = 0

            h400 = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][6] == '400':
                    h400[i] = 1
                else:
                    h400[i] = 0

            h500 = np.zeros(N + 1, dtype=int)
            for i in range(1, N + 1):
                if result_flatten[i - 1][6] == '500':
                    h500[i] = 1
                else:
                    h500[i] = 0

            # # 创建参数o(有货的货格总数）
            # o = 0
            # for i in range(1, N + 1):  # 将i的范围从1到N+1
            #     if result_flatten[i - 1][3] != 0:
            #         o += 1
            # # print("o=", o)

            # # 创建参数e
            # e = 1

            # 创建参数Q
            Q = 100000


            # # 约束条件：模板调整涉及到的货量尽量少
            # model.Add(
            #     sum(mij[i][j] - X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1) if mij[i][j] == 1) <= int(
            #         e * o))

            # 约束条件 - 每个货格只能选择一个货物类型
            for i in range(1, N + 1):
                model.Add(sum(X[(i, j)] for j in range(1, M + 1)) == 1)

            # 计算总货位数
            total_cargo_space = model.NewIntVar(0, sum_total * N, 'total_cargo_space')

            # 添加约束条件，计算总货位数
            model.Add(total_cargo_space == sum(nij[i][j] * X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1)))

            # 新增约束（高热度部分）下限
            for J in range(1, M + 1):
                if available_goods_out[J - 1][:3] == '265':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h265[i] == 1) * int(ahj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '330':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h330[i] == 1) * int(ahj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '400':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h400[i] == 1) * int(ahj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '500':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h500[i] == 1) * int(ahj[J] * Q * f)
                              )
            # 新增约束（中热度部分）下限
            for J in range(1, M + 1):
                if available_goods_out[J - 1][:3] == '265':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h265[i] == 1) * int(amj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '330':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h330[i] == 1) * int(amj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '400':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h400[i] == 1) * int(amj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '500':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h500[i] == 1) * int(amj[J] * Q * f)
                              )

            # 新增约束（低热度部分）下限
            for J in range(1, M + 1):
                if available_goods_out[J - 1][:3] == '265':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h265[i] == 1) * int(alj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '330':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h330[i] == 1) * int(alj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '400':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h400[i] == 1) * int(alj[J] * Q * f)
                              )
                if available_goods_out[J - 1][:3] == '500':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h500[i] == 1) * int(alj[J] * Q * f)
                              )

            #新增约束（高热度部分）上限
            for J in range(1, M + 1):
                if available_goods_out[J - 1][:3] == '265':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h265[i] == 1) * int(ahj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '330':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h330[i] == 1) * int(ahj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '400':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h400[i] == 1) * int(ahj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '500':
                    model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  khi[i] == 1 and h500[i] == 1) * int(ahj[J] * Q * s)
                              )
            # 新增约束（中热度部分）上限
            for J in range(1, M + 1):
                if available_goods_out[J - 1][:3] == '265':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h265[i] == 1) * int(amj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '330':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h330[i] == 1) * int(amj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '400':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h400[i] == 1) * int(amj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '500':
                    model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kmi[i] == 1 and h500[i] == 1) * int(amj[J] * Q * s)
                              )

            # 新增约束（低热度部分）上限
            for J in range(1, M + 1):
                if available_goods_out[J - 1][:3] == '265':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h265[i] == 1) * int(alj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '330':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h330[i] == 1) * int(alj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '400':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h400[i] == 1) * int(alj[J] * Q * s)
                              )
                if available_goods_out[J - 1][:3] == '500':
                    model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                              sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                  kli[i] == 1 and h500[i] == 1) * int(alj[J] * Q * s)
                              )

            # 保证比例为0的不选择
            for j in range(1, M + 1):
                if ahj[j] == 0:
                    for i in range(1, N + 1):
                        if khi[i] == 1:
                            model.Add(X[(i, j)] == 0)

            for j in range(1, M + 1):
                if amj[j] == 0:
                    for i in range(1, N + 1):
                        if kmi[i] == 1:
                            model.Add(X[(i, j)] == 0)

            for j in range(1, M + 1):
                if alj[j] == 0:
                    for i in range(1, N + 1):
                        if kli[i] == 1:
                            model.Add(X[(i, j)] == 0)

            # 优化目标 - 最大化总货位数
            model.Maximize(total_cargo_space)
            logger.info(f'上限约束添加完成')

            # 创建一个cp-sat求解器
            solver = cp_model.CpSolver()
            solver.parameters.log_search_progress = False
            solver.parameters.max_time_in_seconds = 200
            # 解决优化问题
            status = solver.Solve(model)
            logger.info(f'下限约束模型试求解')
            # 输出结果
            if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
                logger.info(f'上限约束解决')
                return f, s
            else:
                if s >= 10:
                    logger.info(f'上限约束不满足')
                    return -1, 0
                else:
                    s = s + 0.1



def do_solve3(result_flatten, element_count, available_goods_out, template_mapping, sum_total, ahj, amj, alj, name, logger=Logger):
# 求解

        f, s = do_solve2(result_flatten, element_count, available_goods_out, template_mapping, sum_total, ahj, amj, alj, logger=logger)
        logger.info(f'do_solve3求解开始')
        if f == 0:
            logger.error(f'[PM] 无法找到最优解,下限约束异常')
            selected_templates = {}
            for i in range(1, element_count + 1):
                selected_templates[i] = result_flatten[i - 1][3]
            return selected_templates, -1
        elif f == -1:
            logger.error(f'[PM] 无法找到最优解,上限约束异常')
            selected_templates = {}
            for i in range(1, element_count + 1):
                selected_templates[i] = result_flatten[i - 1][3]
            return selected_templates, -1
        else:
            e = 0.3
            while True:

                '''
                :param result_flatten:一个一维列表，列表中的元素为元组，从货架顶自左向右，自上而下遍历所有货格，每个元组内的元素为：（层数，货格序号，货格号，选取的货物类型，货量，热度，高度）
                :param element_count:货格总数
                :param available_goods_out:一个一维列表，列表元素为该货架所有的模板类型，格式为“高度*长*宽”
                :param template_mapping:一个字典，定义不同模板号的可放置的货物类型，以及每种货物类型最多放多少货物
                :param sum_total:不考虑高度，所有模板最大放货量的总和，此变量主要是为了给求解器一个范围值，也可用100代替
                :return:每个货格选择的模板
                '''

                K = 100  # 抵消比例的100倍数
                N = element_count
                M = len(available_goods_out)


                # 创建cp-sat模型
                model = cp_model.CpModel()
                logger.info(f'最终约束模型创建完成')




                # 变量Xij表示是否选中货格i的货物类型j，采用布尔类型
                X = {}
                for i in range(1, N + 1):
                    for j in range(1, M + 1):
                        X[(i, j)] = model.NewBoolVar(f'X_{i}_{j}')

                # 判断是否为异常货格
                exi = np.zeros(N + 1, dtype=int)

                # 创建参数nij
                nij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 nij 数组
                nij[0, 1:] = available_goods_out
                nij[1:, 0] = list(range(1, element_count + 1))
                for i in range(1, N + 1):  # 将i的范围从1到N+1
                    if result_flatten[i - 1][2] not in template_mapping[result_flatten[i - 1][6]]:
                        logger.error(f'[PM]{i}号货格为异常尺寸{result_flatten[i - 1][2]},跳过该货格')
                        exi[i] = 1
                    else:
                        for j in range(1, M + 1):  # 将j的范围从1到M+1
                            if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
                                if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][
                                    result_flatten[i - 1][2]]:
                                    nij[i][j] = \
                                    template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                        available_goods_out[j - 1]]
                                else:
                                    nij[i][j] = 0
                                    model.Add(X[(i, j)] == 0)
                            else:  # 如果货格 i 中已经有货，货物类型为 selected_goods
                                if result_flatten[i - 1][3] == available_goods_out[j - 1]:
                                    max_quantity = \
                                    template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                        result_flatten[i - 1][3]]
                                    selected_quantity = result_flatten[i - 1][4]
                                    nij[i][j] = max_quantity - selected_quantity
                                else:
                                    if available_goods_out[j - 1] in template_mapping[result_flatten[i - 1][6]][
                                        result_flatten[i - 1][2]]:
                                        nij[i][j] = \
                                        template_mapping[result_flatten[i - 1][6]][result_flatten[i - 1][2]][
                                            available_goods_out[j - 1]]
                                    else:
                                        nij[i][j] = 0  # 其他类型的货物不能放在已经有货的货格中
                                        model.Add(X[(i, j)] == 0)

                # for i in range(0, N + 1):
                #     print(nij[i, :])

                # 创建参数mij
                mij = np.zeros((N + 1, M + 1), dtype=object)  # 初始化 mij 数组
                mij[0, 1:] = available_goods_out
                mij[1:, 0] = list(range(1, element_count + 1))
                d = 3
                for i in range(1, N + 1):  # 将i的范围从1到N+1
                    for j in range(1, M + 1):  # 将j的范围从1到M+1
                        if result_flatten[i - 1][3] == 0:  # 如果货格 i 中没有货
                            mij[i][j] = 0
                        else:
                            if result_flatten[i - 1][3] == available_goods_out[j - 1]:
                                # if result_flatten[i - 1][4] <= d:
                                mij[i][j] = 1

                            else:
                                mij[i][j] = 0

                # for i in range(0, N + 1):
                #     print(mij[i, :])

                # 创建参数khi,kmi,kli,判断货格i是否为高中低热度
                khi = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][5] == 2:
                        khi[i] = 1
                    else:
                        khi[i] = 0

                kmi = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][5] == 1:
                        kmi[i] = 1
                    else:
                        kmi[i] = 0

                kli = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][5] == 0:
                        kli[i] = 1
                    else:
                        kli[i] = 0

                # 创建参数,判断货格i是否为对应高度
                h265 = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][6] == '265':
                        h265[i] = 1
                    else:
                        h265[i] = 0

                h330 = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][6] == '330':
                        h330[i] = 1
                    else:
                        h330[i] = 0

                h400 = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][6] == '400':
                        h400[i] = 1
                    else:
                        h400[i] = 0

                h500 = np.zeros(N + 1, dtype=int)
                for i in range(1, N + 1):
                    if result_flatten[i - 1][6] == '500':
                        h500[i] = 1
                    else:
                        h500[i] = 0

                # 创建参数o(有货的货格总数）
                o = 0
                for i in range(1, N + 1):  # 将i的范围从1到N+1
                    if result_flatten[i - 1][3] != 0:
                        o += 1
                # print("o=", o)


                # 创建参数Q
                Q = 100000

                # 约束条件：模板调整涉及到的货量尽量少
                model.Add(
                    sum(mij[i][j] - X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1) if mij[i][j] == 1) <= int(e * o))

                # 约束条件 - 每个货格只能选择一个货物类型
                for i in range(1, N + 1):
                    model.Add(sum(X[(i, j)] for j in range(1, M + 1)) == 1)

                # 计算总货位数
                total_cargo_space = model.NewIntVar(0, sum_total * N, 'total_cargo_space')

                # 添加约束条件，计算总货位数
                model.Add(total_cargo_space == sum(nij[i][j] * X[(i, j)] for i in range(1, N + 1) for j in range(1, M + 1)))



                #新增约束（高热度部分）下限
                for J in range(1, M + 1):
                    if available_goods_out[J - 1][:3] == '265':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h265[i] == 1) * int(ahj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '330':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h330[i] == 1) * int(ahj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '400':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h400[i] == 1) * int(ahj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '500':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h500[i] == 1) * int(ahj[J] * Q * f)
                                  )
                # 新增约束（中热度部分）下限
                for J in range(1, M + 1):
                    if available_goods_out[J - 1][:3] == '265':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h265[i] == 1) * int(amj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '330':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h330[i] == 1) * int(amj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '400':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h400[i] == 1) * int(amj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '500':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h500[i] == 1) * int(amj[J] * Q * f)
                                  )

                # 新增约束（低热度部分）下限
                for J in range(1, M + 1):
                    if available_goods_out[J - 1][:3] == '265':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h265[i] == 1) * int(alj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '330':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h330[i] == 1) * int(alj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '400':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h400[i] == 1) * int(alj[J] * Q * f)
                                  )
                    if available_goods_out[J - 1][:3] == '500':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q >=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h500[i] == 1) * int(alj[J] * Q * f)
                                  )

                #新增约束（高热度部分）
                for J in range(1, M + 1):
                    if available_goods_out[J - 1][:3] == '265':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h265[i] == 1) * int(ahj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '330':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h330[i] == 1) * int(ahj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '400':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h400[i] == 1) * int(ahj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '500':
                        model.Add(sum(X[(i, J)] * khi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      khi[i] == 1 and h500[i] == 1) * int(ahj[J] * Q * s)
                                  )
                # 新增约束（中热度部分）
                for J in range(1, M + 1):
                    if available_goods_out[J - 1][:3] == '265':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h265[i] == 1) * int(amj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '330':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h330[i] == 1) * int(amj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '400':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h400[i] == 1) * int(amj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '500':
                        model.Add(sum(X[(i, J)] * kmi[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kmi[i] == 1 and h500[i] == 1) * int(amj[J] * Q * s)
                                  )

                # 新增约束（低热度部分）
                for J in range(1, M + 1):
                    if available_goods_out[J - 1][:3] == '265':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h265[i] == 1) * int(alj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '330':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h330[i] == 1) * int(alj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '400':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h400[i] == 1) * int(alj[J] * Q * s)
                                  )
                    if available_goods_out[J - 1][:3] == '500':
                        model.Add(sum(X[(i, J)] * kli[i] * nij[i][J] for i in range(1, N + 1)) * Q <=
                                  sum(X[(i, j)] * nij[i][j] for i in range(1, N + 1) for j in range(1, M + 1) if
                                      kli[i] == 1 and h500[i] == 1) * int(alj[J] * Q * s)
                                  )




                # 保证比例为0的不选择
                for j in range(1, M + 1):
                    if ahj[j] == 0:
                        for i in range(1, N + 1):
                            if khi[i] == 1:
                                model.Add(X[(i, j)] == 0)

                for j in range(1, M + 1):
                    if amj[j] == 0:
                        for i in range(1, N + 1):
                            if kmi[i] == 1:
                                model.Add(X[(i, j)] == 0)

                for j in range(1, M + 1):
                    if alj[j] == 0:
                        for i in range(1, N + 1):
                            if kli[i] == 1:
                                model.Add(X[(i, j)] == 0)

                logger.info(f'最终约束添加完成')


                # 优化目标 - 最大化总货位数
                model.Maximize(total_cargo_space)

                # 创建一个cp-sat求解器
                solver = cp_model.CpSolver()
                solver.parameters.log_search_progress = False
                solver.parameters.max_time_in_seconds = 200
                solver.parameters.num_workers
                # 解决优化问题
                status = solver.Solve(model)
                logger.info(f'最终约束模型试求解')
                selected_templates = {}
                # 输出结果
                if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
                    for i in range(1, N + 1):
                        for j in range(1, M + 1):
                            if exi[i] == 1:
                                selected_templates[i] = result_flatten[i - 1][3]
                            else:
                                if solver.Value(X[(i, j)]) == 1:
                                    selected_templates[i] = available_goods_out[j - 1]
                    logger.info(f'[PM] f={f}')
                    logger.info(f'[PM] s={s}')
                    logger.info(f'[PM] e={e}')
                    return selected_templates, solver.ObjectiveValue()


                else:
                    if e >= 1.5:
                        logger.error(f'[PM] 无法找到最优解，模板变更约束异常')
                        selected_templates = {}
                        for i in range(1, element_count + 1):
                            selected_templates[i] = result_flatten[i - 1][3]
                        return selected_templates, -1
                    else:
                        e = e + 0.05


def solve(data_set, logger=Logger):
    result1 = []
    result2 = []
    size = len(data_set)
    for idx, data in enumerate(data_set):
        ziph = zip(data[2], data[5][1:])
        zipm = zip(data[2], data[6][1:])
        zipl = zip(data[2], data[7][1:])
        dict_h = dict(ziph)
        dict_m = dict(zipm)
        dict_l = dict(zipl)

        # 将不合理比例（货架不能放置，但给出比例不为0）置为0
        for good in data[2]:
            if good[:3] in data[3]:  # 防止某一高度根本没有
                inner_keys = [inner_key for inner_dict in data[3][good[:3]].values() for inner_key in
                              inner_dict.keys()]
                if good not in inner_keys:
                    logger.error(f'[PM] {good} 模板不在template_mapping可用模板中，比例置零')
                    dict_h[good] = 0
                    dict_m[good] = 0
                    dict_l[good] = 0
            else:
                logger.error(f'[PM] {good} 模板不在template_mapping可用模板中，比例置零')
                dict_h[good] = 0
                dict_m[good] = 0
                dict_l[good] = 0

        # 将每个高度的货格号整理在一个字典
        exist_list = []
        exist_dict = {}
        for high in data[3]:
            for result in data[0]:
                if result[6] == high:
                    exist_list.append(result[2])
            exist_dict[high] = exist_list
            exist_list = []
        # print(exist_dict)
        # print(list(exist_dict.keys()))
        # print(len(exist_dict['400']))
        # print(len(exist_dict['330']))
        # print(len(exist_dict['265']))
        # print(len(exist_dict['500']))

        for high in data[3]:
            for item in data[3][high]:
                if item not in exist_dict[high]:
                    logger.error(f'[PM]template_mapping可用模板中，高度 {high} 货格号 {item} 在货架中不存在，仅在该货格可用的模板置零')
                    for template in data[3][high][item]:  # 保证模板只能用于不存在的可用货格
                        count = 0
                        for value in data[3][high].values():
                            if template in value:
                                count += 1
                        if count == 1:
                            dict_h[template] = 0
                            dict_m[template] = 0
                            dict_l[template] = 0





        # 空字典，存放归一化所需同高度比例之和
        num_h = {}
        num_m = {}
        num_l = {}

        # 遍历原始字典，将键的前三个字符相同的值相加
        for key, value in dict_h.items():
            prefix = key[:3]  # 获取键的前三个字符
            if prefix not in num_h:
                num_h[prefix] = 0
            num_h[prefix] += value

        for key, value in dict_m.items():
            prefix = key[:3]  # 获取键的前三个字符
            if prefix not in num_m:
                num_m[prefix] = 0
            num_m[prefix] += value

        for key, value in dict_l.items():
            prefix = key[:3]  # 获取键的前三个字符
            if prefix not in num_l:
                num_l[prefix] = 0
            num_l[prefix] += value

        # 归一化处理
        for key in dict_h:
            if num_h[key[:3]] != 0:
                dict_h[key] = round(dict_h[key] / num_h[key[:3]], 2)

        for key in dict_m:
            if num_m[key[:3]] != 0:
                dict_m[key] = round(dict_m[key] / num_m[key[:3]], 2)

        for key in dict_l:
            if num_l[key[:3]] != 0:
                dict_l[key] = round(dict_l[key] / num_l[key[:3]], 2)

        ahj_new = [0]
        amj_new = [0]
        alj_new = [0]
        for i in data[2]:
            ahj_new.append(dict_h[i])
            amj_new.append(dict_m[i])
            alj_new.append(dict_l[i])

        # 创建一个新的元组，替换原始元组中的比例列表
        new_data = data[:5] + (ahj_new,) + (amj_new,) + (alj_new,) + data[8:]
        data = new_data

        logger.info(f'[PM] 实际应用的高热度比例数据为： {data[5]} ')
        logger.info(f'[PM] 实际应用的中热度比例数据为： {data[6]} ')
        logger.info(f'[PM] 实际应用的低热度比例数据为： {data[7]} ')




        print(data[2])
        value_dict = {}
        for model in data[2]:
            value_dict[(model[:3], 0)] = {}
            value_dict[(model[:3], 1)] = {}
            value_dict[(model[:3], 2)] = {}
        # print('value_dict :', value_dict)
        tunnel_list = data[0]  # 货架数据
        for box in tunnel_list:  # 遍历每一个货格
            index = (box[6], box[5])  # 货格的高度，热度组成一个元组
            Available = copy.deepcopy(data[3][box[6]][box[2]])  # 该货格可用的模板，一个字典
            # 加工一下可用模板，若货格有对应的货物，要减去对应的值
            for Template in Available:
                if Template == box[3]:
                    Available[Template] = Available[Template] - box[4]
            for Template in Available:
                if Template in value_dict[index]:
                    value_dict[index][Template] += Available[Template]
                else:
                    value_dict[index][Template] = Available[Template]


        ratio_dict = value_dict.copy()
        for key in ratio_dict:
            total = sum(ratio_dict[key].values())
            ratio_dict[key] = {key: round(value / total, 2) for key, value in ratio_dict[key].items()}

        # print('value_dict :', value_dict)
        # print('ratio_dict :', ratio_dict)

        for KEY in ratio_dict:
            if not ratio_dict[KEY]:
                logger.info(f'{KEY[0]} 高度 {KEY[1]} 热度没有可用货格')
            else:
                for key in ratio_dict[KEY]:
                    logger.info(f'{KEY[0]} 高度 {KEY[1]} 热度,{key}参考比例为{ratio_dict[KEY][key]}')




        logger.info(f'[PM] 巷道 {data[8]} ({size}-{idx+1}) 开始排模')
        selected_templates, ObjectiveValue = do_solve3(*data, logger=logger)
        if ObjectiveValue == -1:
            logger.info(f'[PM] 巷道 {data[8]} ({size}-{idx + 1}) 未排摸，按原始数据输出')
        else:
            logger.info(f'[PM] 巷道 {data[8]} ({size}-{idx+1}) 已排模')
        result1.append(selected_templates)
        result2.append(ObjectiveValue)
    return result1, result2
